﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using RServer.IVSUsageService;
using RServer.Properties;
using System.Security.Cryptography;

namespace RServer
{
    public class RService : IRService
    {
        private string workingDir = AppDomain.CurrentDomain.GetData("DataDirectory").ToString() + @"\";

        //read in the character conversion stuff
        private string[] charConverterLines = File.ReadAllLines(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "CharacterConversion.txt");

        public RService() { }

        public void UploadCSVData(Guid ID, string[] csv)
        {
            //determine the filename
            string fileName = workingDir + ID.ToString() + ".csv";
            
            //go through and replace the first line...
            foreach (string s in charConverterLines)
            {
                string find = s.Substring(0, 1);
                string replace = s.Substring(2);

                csv[0] = csv[0].Replace(find, replace);
            }

            //...then write it out
            File.WriteAllLines(fileName, csv);
        }

        public string ExecuteR(Guid ID, string scriptName, Dictionary<string, string> arguments)
        {
            Stopwatch sw = Stopwatch.StartNew();

            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = Settings.Default.RScriptExeLocation;
            psi.WorkingDirectory = Path.GetTempPath();

            string scriptDir = AppDomain.CurrentDomain.BaseDirectory + "scripts";
            string scriptFileName = scriptDir + @"\" + scriptName;

            if(!scriptFileName.ToUpper().EndsWith(".R")) scriptFileName = scriptFileName + ".R";

            scriptFileName = "\"" + scriptFileName + "\"";

            string csvFileName = "\"" + workingDir + ID.ToString() + ".csv" + "\"";

            string theArguments = " ";
            foreach (string arg in arguments.Values)
            {
                theArguments = theArguments + " " + arg;
            }

            psi.Arguments = scriptFileName + " --vanilla --args " + csvFileName + theArguments;

            //Configure some options for the R process
            psi.UseShellExecute = false;
            psi.CreateNoWindow = true;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;

            //create a stringbuilder to hold the output, and get the output line by line until R process finishes
            StringBuilder output = new StringBuilder();

            Process R = Process.Start(psi);
            DateTime timeOutTime = DateTime.Now.AddSeconds(Properties.Settings.Default.RTimeout);

            while (!R.HasExited || (DateTime.Now > timeOutTime))
            {
                Thread.Sleep(50);
            }

            //need to make sure that we have got all the output so do a readtoend here
            output.AppendLine(R.StandardOutput.ReadToEnd());
            output.AppendLine();

            //output the errors from R
            string errorsFromR = R.StandardError.ReadToEnd().Trim();

            if (!String.IsNullOrEmpty(errorsFromR))
            {
                output.AppendLine();
                output.AppendLine("The following warnings/errors were generated by the R processor:");
                output.AppendLine();
                output.Append(errorsFromR);
                output.AppendLine();
            }

            TimeSpan timeElapsed = sw.Elapsed;
            output.AppendLine("Data analysis by R Server took " + Math.Round(timeElapsed.TotalSeconds, 2) + " seconds.");

            R.Close();

            //assemble the entire path and file to the html output
            string htmlFile = GetHTMLReport(ID);

            if (File.Exists(htmlFile)) //wont exist if there is an error!
            {
                //the replace dodgy characters back to real characters!
                string theHTML = File.ReadAllText(htmlFile);
                foreach (string s in charConverterLines)
                {
                    string find = s.Substring(2);
                    string replace = s.Substring(0, 1);

                    theHTML = theHTML.Replace(find, replace);
                }

                File.WriteAllText(htmlFile, theHTML);
            }

            //send usage info to the database (use thread)
            UsageInfo info = new UsageInfo();
            info.Version = "1.0";
            info.UserName = "SilveR";
            info.ScriptName = scriptName;
            info.Arguments = arguments;
            info.ScriptErrors = errorsFromR;

            Thread thread = new Thread(SendUsageInfo);
            thread.Start(info);

            return output.ToString();
        }

        private void SendUsageInfo(object oInfo)
        {
            UsageInfo info = (UsageInfo)oInfo;
            IVSUsageServiceClient client = null;

            try
            {
                //client = new IVSUsageServiceClient(new BasicHttpBinding(), new EndpointAddress("http://bigpuss.homeip.net/ivsusageservice/ivsusageservice.svc"));
                client = new IVSUsageServiceClient();
                client.SendUnencryptedUsageInfo(info);
                client.Close();

                client = null;
            }
            catch (Exception)
            {
            }

            client = null;
        }

        public string GetHTMLReport(Guid ID)
        {
            return workingDir + ID.ToString() + ".html";
        }

        private DataTable ReadInCSVData(CSVReader csv)
        {
            DataTable dt = new DataTable("Data");

            try
            {
                //use the csvreader to read in the csv data
                string[] fields;
                while ((fields = csv.GetCSVLine()) != null)
                {
                    if (dt.Columns.Count == 0) //the first row will always be treated as column headers
                    {
                        for (int i = 0; i < fields.Length; i++)
                        {
                            dt.Columns.Add(new DataColumn(fields[i]));
                        }
                    }
                    else
                    {
                        dt.Rows.Add(fields); //add the row to the datatable
                    }
                }
            }
            catch (DuplicateNameException)
            {
                //probably need to do error logging here
                return null;
            }

            return dt;
        }

        public void ReportError(string errorInfo)
        {
            File.WriteAllText(workingDir + DateTime.Now.ToString(), errorInfo);
        }

        public string[] GetCharConversionList()
        {
            return charConverterLines;
        }

        public string GetReportsFolder()
        {
            return Properties.Settings.Default.ReportsFolder;
        }


        //Script Editing Methods...
        public List<string> GetListOfScripts()
        {
            string scriptDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts");

            var scriptFiles = Directory.GetFiles(scriptDir);

            List<string> fileList = new List<string>();

            foreach (string s in scriptFiles)
            {
                FileInfo file = new FileInfo(s);
                fileList.Add(file.Name);
            }

            return fileList;
        }

        public string GetRScript(string scriptName)
        {
            string scriptLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts", scriptName);

            return File.ReadAllText(Path.Combine(scriptLocation));
        }

        public void SaveRScript(string scriptName, List<string> scriptLines)
        {
            string scriptLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts", scriptName);

            string renamedScriptLocation = scriptLocation.TrimEnd('R') + DateTime.Now.ToString("ddMMMyy HH-mm-ss") + ".R";

            //rename the existing script
            File.Move(scriptLocation, renamedScriptLocation);

            File.WriteAllLines(scriptLocation, scriptLines);
        }
    }
}